#include "stdafx.h"
#include "WindowMsgLoop.h"

void Msg_Init(WindowMsgLoop::MSGMAP& msgMap);

WindowMsgLoop::WindowMsgLoop()
    : m_nDisablePumpCount(0)
    , m_nMsgLast(WM_NULL)
{
    Msg_Init(m_msgMap);
}

void msg_timer(evutil_socket_t fd, short event, void *arg)
{
    WindowMsgLoop *loop = (WindowMsgLoop *)arg;
    if (loop->Event())
    {
        struct timeval tv;
        evutil_timerclear(&tv);
        tv.tv_sec = 0;
        tv.tv_usec = 10;
        event_add(&loop->msgtimer, &tv);
    }
}

void WindowMsgLoop::Run()
{
    WORD wVersionRequested;
    WSADATA wsaData;
    wVersionRequested = MAKEWORD(2, 2);
    WSAStartup(wVersionRequested, &wsaData);

    struct timeval tv;
    evtimer_assign(&msgtimer, evbase, &msg_timer, this);
    evutil_timerclear(&tv);
    tv.tv_sec = 0;
    tv.tv_usec = 10;
    evtimer_add(&msgtimer, &tv);
    event_base_dispatch(evbase);

    WSACleanup();
}

BOOL WindowMsgLoop::Event()
{
    if (!PeekMessage(&m_msg, NULL, NULL, NULL, PM_NOREMOVE))
        return TRUE;

    return PumpMessage();
}

BOOL WindowMsgLoop::PumpMessage()
{
    if (!GetMessage(&m_msg, NULL, NULL, NULL))
    {
        m_nDisablePumpCount++; // application must die
                               // Note: prevents calling message loop things in 'ExitInstance'
                               // will never be decremented
        return FALSE;
    }

    if (m_nDisablePumpCount != 0)
    {
        ATLTRACE("Error: PumpMessage called when not permitted.");
        ATLASSERT(FALSE);
    }

    // process this message
    TranslateMessage(&m_msg);

#ifdef _DEBUG
    if (m_msg.message != WM_PAINT &&
        m_msg.message != WM_SETCURSOR &&
        m_msg.message != WM_NCHITTEST &&
        m_msg.message != WM_MOUSEMOVE &&
        m_msg.message != WM_MOUSEHOVER &&
        m_msg.message != WM_NCMOUSELEAVE &&
        m_msg.message != WM_SYSTIMER &&
        m_msg.message != WM_TIMER &&
        m_msg.message != WM_NCMOUSEMOVE)
    {
        ATLTRACE(atlTraceWindowing, 0, "hwnd: 0x%08x message: %-30S[0x%04x] {W:0x%08X,L:0x%08X}\n", m_msg.hwnd,
            DebugGetMessageName(m_msg.message), m_msg.message, m_msg.wParam, m_msg.lParam);
    }
#endif // _DEBUG

    DispatchMessage(&m_msg);

    return TRUE;
}

BOOL WindowMsgLoop::IsIdleMessage(MSG* pMsg)
{
    // Return FALSE if the message just dispatched should _not_
    // cause OnIdle to be run.  Messages which do not usually
    // affect the state of the user interface and happen very
    // often are checked for.

    // redundant WM_MOUSEMOVE and WM_NCMOUSEMOVE
    if (pMsg->message == WM_MOUSEMOVE || pMsg->message == WM_NCMOUSEMOVE || pMsg->message == WM_MOUSEWHEEL)
    {
        // mouse move at same position as last mouse move?
        if (m_ptMousePos == pMsg->pt && pMsg->message == m_nMsgLast)
            return FALSE;

        m_ptMousePos = pMsg->pt;  // remember for next time
        m_nMsgLast = pMsg->message;
        return TRUE;
    }

    // WM_PAINT and WM_SYSTIMER (caret blink)
    return pMsg->message != WM_PAINT && pMsg->message != 0x0118;
}

BOOL WindowMsgLoop::OnIdle(LONG lCount)
{
    return lCount < 0;
}

struct X_MAP_MESSAGE
{
    UINT    nMsg;
    LPCTSTR lpszMsg;
};

#ifdef _UNICODE
#define DEFINE_MESSAGE(wm)  { wm, _T(#wm) }
#else
#define DEFINE_MESSAGE(wm)  { wm, #wm }
#endif

// All message text
static const X_MAP_MESSAGE allMessages[] =
{
    DEFINE_MESSAGE(WM_NULL),
    DEFINE_MESSAGE(WM_CREATE),
    DEFINE_MESSAGE(WM_DESTROY),
    DEFINE_MESSAGE(WM_MOVE),
    DEFINE_MESSAGE(WM_SIZE),
    DEFINE_MESSAGE(WM_ACTIVATE),
    DEFINE_MESSAGE(WM_SETFOCUS),
    DEFINE_MESSAGE(WM_KILLFOCUS),
    DEFINE_MESSAGE(WM_ENABLE),
    DEFINE_MESSAGE(WM_SETREDRAW),
    DEFINE_MESSAGE(WM_SETTEXT),
    DEFINE_MESSAGE(WM_GETTEXT),
    DEFINE_MESSAGE(WM_GETTEXTLENGTH),
    DEFINE_MESSAGE(WM_PAINT),
    DEFINE_MESSAGE(WM_CLOSE),
    DEFINE_MESSAGE(WM_QUERYENDSESSION),
    DEFINE_MESSAGE(WM_QUIT),
    DEFINE_MESSAGE(WM_QUERYOPEN),
    DEFINE_MESSAGE(WM_ERASEBKGND),
    DEFINE_MESSAGE(WM_SYSCOLORCHANGE),
    DEFINE_MESSAGE(WM_ENDSESSION),
    DEFINE_MESSAGE(WM_SHOWWINDOW),
    DEFINE_MESSAGE(WM_CTLCOLORMSGBOX),
    DEFINE_MESSAGE(WM_CTLCOLOREDIT),
    DEFINE_MESSAGE(WM_CTLCOLORLISTBOX),
    DEFINE_MESSAGE(WM_CTLCOLORBTN),
    DEFINE_MESSAGE(WM_CTLCOLORDLG),
    DEFINE_MESSAGE(WM_CTLCOLORSCROLLBAR),
    DEFINE_MESSAGE(WM_CTLCOLORSTATIC),
    DEFINE_MESSAGE(OCM_COMMAND),
    DEFINE_MESSAGE(OCM_CTLCOLORBTN),
    DEFINE_MESSAGE(OCM_CTLCOLOREDIT),
    DEFINE_MESSAGE(OCM_CTLCOLORDLG),
    DEFINE_MESSAGE(OCM_CTLCOLORLISTBOX),
    DEFINE_MESSAGE(OCM_CTLCOLORMSGBOX),
    DEFINE_MESSAGE(OCM_CTLCOLORSCROLLBAR),
    DEFINE_MESSAGE(OCM_CTLCOLORSTATIC),
    DEFINE_MESSAGE(OCM_DRAWITEM),
    DEFINE_MESSAGE(OCM_MEASUREITEM),
    DEFINE_MESSAGE(OCM_DELETEITEM),
    DEFINE_MESSAGE(OCM_VKEYTOITEM),
    DEFINE_MESSAGE(OCM_CHARTOITEM),
    DEFINE_MESSAGE(OCM_COMPAREITEM),
    DEFINE_MESSAGE(OCM_HSCROLL),
    DEFINE_MESSAGE(OCM_VSCROLL),
    DEFINE_MESSAGE(OCM_PARENTNOTIFY),
    DEFINE_MESSAGE(OCM_NOTIFY),
    DEFINE_MESSAGE(WM_WININICHANGE),
    DEFINE_MESSAGE(WM_SETTINGCHANGE),
    DEFINE_MESSAGE(WM_DEVMODECHANGE),
    DEFINE_MESSAGE(WM_ACTIVATEAPP),
    DEFINE_MESSAGE(WM_FONTCHANGE),
    DEFINE_MESSAGE(WM_TIMECHANGE),
    DEFINE_MESSAGE(WM_CANCELMODE),
    DEFINE_MESSAGE(WM_SETCURSOR),
    DEFINE_MESSAGE(WM_MOUSEACTIVATE),
    DEFINE_MESSAGE(WM_CHILDACTIVATE),
    DEFINE_MESSAGE(WM_QUEUESYNC),
    DEFINE_MESSAGE(WM_GETMINMAXINFO),
    DEFINE_MESSAGE(WM_ICONERASEBKGND),
    DEFINE_MESSAGE(WM_NEXTDLGCTL),
    DEFINE_MESSAGE(WM_SPOOLERSTATUS),
    DEFINE_MESSAGE(WM_DRAWITEM),
    DEFINE_MESSAGE(WM_MEASUREITEM),
    DEFINE_MESSAGE(WM_DELETEITEM),
    DEFINE_MESSAGE(WM_VKEYTOITEM),
    DEFINE_MESSAGE(WM_CHARTOITEM),
    DEFINE_MESSAGE(WM_SETFONT),
    DEFINE_MESSAGE(WM_GETFONT),
    DEFINE_MESSAGE(WM_QUERYDRAGICON),
    DEFINE_MESSAGE(WM_COMPAREITEM),
    DEFINE_MESSAGE(WM_COMPACTING),
    DEFINE_MESSAGE(WM_NCCREATE),
    DEFINE_MESSAGE(WM_NCDESTROY),
    DEFINE_MESSAGE(WM_NCCALCSIZE),
    DEFINE_MESSAGE(WM_NCHITTEST),
    DEFINE_MESSAGE(WM_NCPAINT),
    DEFINE_MESSAGE(WM_NCACTIVATE),
    DEFINE_MESSAGE(WM_GETDLGCODE),
    DEFINE_MESSAGE(WM_NCMOUSEMOVE),
    DEFINE_MESSAGE(WM_NCLBUTTONDOWN),
    DEFINE_MESSAGE(WM_NCLBUTTONUP),
    DEFINE_MESSAGE(WM_NCLBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_NCRBUTTONDOWN),
    DEFINE_MESSAGE(WM_NCRBUTTONUP),
    DEFINE_MESSAGE(WM_NCRBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_NCMBUTTONDOWN),
    DEFINE_MESSAGE(WM_NCMBUTTONUP),
    DEFINE_MESSAGE(WM_NCMBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_KEYDOWN),
    DEFINE_MESSAGE(WM_KEYUP),
    DEFINE_MESSAGE(WM_CHAR),
    DEFINE_MESSAGE(WM_DEADCHAR),
    DEFINE_MESSAGE(WM_SYSKEYDOWN),
    DEFINE_MESSAGE(WM_SYSKEYUP),
    DEFINE_MESSAGE(WM_SYSCHAR),
    DEFINE_MESSAGE(WM_SYSDEADCHAR),
    DEFINE_MESSAGE(WM_KEYLAST),
    DEFINE_MESSAGE(WM_INITDIALOG),
    DEFINE_MESSAGE(WM_COMMAND),
    DEFINE_MESSAGE(WM_SYSCOMMAND),
    DEFINE_MESSAGE(WM_TIMER),
    DEFINE_MESSAGE(WM_HSCROLL),
    DEFINE_MESSAGE(WM_VSCROLL),
    DEFINE_MESSAGE(WM_INITMENU),
    DEFINE_MESSAGE(WM_INITMENUPOPUP),
    DEFINE_MESSAGE(WM_MENURBUTTONUP),
    DEFINE_MESSAGE(WM_MENUDRAG),
    DEFINE_MESSAGE(WM_MENUGETOBJECT),
    DEFINE_MESSAGE(WM_UNINITMENUPOPUP),
    DEFINE_MESSAGE(WM_MENUCOMMAND),
    DEFINE_MESSAGE(WM_MENUSELECT),
    DEFINE_MESSAGE(WM_MENUCHAR),
    DEFINE_MESSAGE(WM_ENTERIDLE),
    DEFINE_MESSAGE(WM_MOUSEWHEEL),
    DEFINE_MESSAGE(WM_MOUSEMOVE),
    DEFINE_MESSAGE(WM_LBUTTONDOWN),
    DEFINE_MESSAGE(WM_LBUTTONUP),
    DEFINE_MESSAGE(WM_LBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_RBUTTONDOWN),
    DEFINE_MESSAGE(WM_RBUTTONUP),
    DEFINE_MESSAGE(WM_RBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_MBUTTONDOWN),
    DEFINE_MESSAGE(WM_MBUTTONUP),
    DEFINE_MESSAGE(WM_MBUTTONDBLCLK),
    DEFINE_MESSAGE(WM_PARENTNOTIFY),
    DEFINE_MESSAGE(WM_MDICREATE),
    DEFINE_MESSAGE(WM_MDIDESTROY),
    DEFINE_MESSAGE(WM_MDIACTIVATE),
    DEFINE_MESSAGE(WM_MDIRESTORE),
    DEFINE_MESSAGE(WM_MDINEXT),
    DEFINE_MESSAGE(WM_MDIMAXIMIZE),
    DEFINE_MESSAGE(WM_MDITILE),
    DEFINE_MESSAGE(WM_MDICASCADE),
    DEFINE_MESSAGE(WM_MDIICONARRANGE),
    DEFINE_MESSAGE(WM_MDIGETACTIVE),
    DEFINE_MESSAGE(WM_MDISETMENU),
    DEFINE_MESSAGE(WM_ENTERSIZEMOVE),
    DEFINE_MESSAGE(WM_EXITSIZEMOVE),
    DEFINE_MESSAGE(WM_DROPFILES),
    DEFINE_MESSAGE(WM_MDIREFRESHMENU),
    DEFINE_MESSAGE(WM_CUT),
    DEFINE_MESSAGE(WM_COPYDATA),
    DEFINE_MESSAGE(WM_COPY),
    DEFINE_MESSAGE(WM_PASTE),
    DEFINE_MESSAGE(WM_CLEAR),
    DEFINE_MESSAGE(WM_UNDO),
    DEFINE_MESSAGE(WM_RENDERFORMAT),
    DEFINE_MESSAGE(WM_RENDERALLFORMATS),
    DEFINE_MESSAGE(WM_DESTROYCLIPBOARD),
    DEFINE_MESSAGE(WM_DRAWCLIPBOARD),
    DEFINE_MESSAGE(WM_PAINTCLIPBOARD),
    DEFINE_MESSAGE(WM_VSCROLLCLIPBOARD),
    DEFINE_MESSAGE(WM_SIZECLIPBOARD),
    DEFINE_MESSAGE(WM_ASKCBFORMATNAME),
    DEFINE_MESSAGE(WM_CHANGECBCHAIN),
    DEFINE_MESSAGE(WM_HSCROLLCLIPBOARD),
    DEFINE_MESSAGE(WM_QUERYNEWPALETTE),
    DEFINE_MESSAGE(WM_PALETTEISCHANGING),
    DEFINE_MESSAGE(WM_PALETTECHANGED),
    DEFINE_MESSAGE(WM_DROPFILES),
    DEFINE_MESSAGE(WM_POWER),
    DEFINE_MESSAGE(WM_WINDOWPOSCHANGED),
    DEFINE_MESSAGE(WM_WINDOWPOSCHANGING),
    // MFC specific messages
    DEFINE_MESSAGE(WM_HELP),
    DEFINE_MESSAGE(WM_NOTIFY),
    DEFINE_MESSAGE(WM_CONTEXTMENU),
    DEFINE_MESSAGE(WM_TCARD),
    DEFINE_MESSAGE(WM_MDIREFRESHMENU),
    DEFINE_MESSAGE(WM_MOVING),
    DEFINE_MESSAGE(WM_STYLECHANGED),
    DEFINE_MESSAGE(WM_STYLECHANGING),
    DEFINE_MESSAGE(WM_SIZING),
    DEFINE_MESSAGE(WM_SETHOTKEY),
    DEFINE_MESSAGE(WM_PRINT),
    DEFINE_MESSAGE(WM_PRINTCLIENT),
    DEFINE_MESSAGE(WM_POWERBROADCAST),
    DEFINE_MESSAGE(WM_HOTKEY),
    DEFINE_MESSAGE(WM_GETICON),
    DEFINE_MESSAGE(WM_EXITMENULOOP),
    DEFINE_MESSAGE(WM_ENTERMENULOOP),
    DEFINE_MESSAGE(WM_DISPLAYCHANGE),
    DEFINE_MESSAGE(WM_STYLECHANGED),
    DEFINE_MESSAGE(WM_STYLECHANGING),
    DEFINE_MESSAGE(WM_GETICON),
    DEFINE_MESSAGE(WM_SETICON),
    DEFINE_MESSAGE(WM_SIZING),
    DEFINE_MESSAGE(WM_MOVING),
    DEFINE_MESSAGE(WM_CAPTURECHANGED),
    DEFINE_MESSAGE(WM_DEVICECHANGE),
    DEFINE_MESSAGE(WM_PENWINFIRST),
    DEFINE_MESSAGE(WM_PENWINLAST),

#if(WINVER >= 0x0400)
    DEFINE_MESSAGE(WM_PRINT),
    DEFINE_MESSAGE(WM_PRINTCLIENT),
#endif /* WINVER >= 0x0400 */

#if(_WIN32_WINNT >= 0x0500)
    DEFINE_MESSAGE(WM_APPCOMMAND),
#endif /* _WIN32_WINNT >= 0x0500 */

#if(_WIN32_WINNT >= 0x0501)
    DEFINE_MESSAGE(WM_THEMECHANGED),
#endif /* _WIN32_WINNT >= 0x0501 */

#if(_WIN32_WINNT >= 0x0501)
    DEFINE_MESSAGE(WM_CLIPBOARDUPDATE),
#endif /* _WIN32_WINNT >= 0x0501 */

#if(_WIN32_WINNT >= 0x0600)
    DEFINE_MESSAGE(WM_DWMCOMPOSITIONCHANGED),
    DEFINE_MESSAGE(WM_DWMNCRENDERINGCHANGED),
    DEFINE_MESSAGE(WM_DWMCOLORIZATIONCOLORCHANGED),
    DEFINE_MESSAGE(WM_DWMWINDOWMAXIMIZEDCHANGE),
#endif /* _WIN32_WINNT >= 0x0600 */

#if(_WIN32_WINNT >= 0x0601)
    DEFINE_MESSAGE(WM_DWMSENDICONICTHUMBNAIL),
    DEFINE_MESSAGE(WM_DWMSENDICONICLIVEPREVIEWBITMAP),
#endif /* _WIN32_WINNT >= 0x0601 */

#if(WINVER >= 0x0600)
    DEFINE_MESSAGE(WM_GETTITLEBARINFOEX),
#endif /* WINVER >= 0x0600 */

#if(WINVER >= 0x0400)
    DEFINE_MESSAGE(WM_HANDHELDFIRST),
    DEFINE_MESSAGE(WM_HANDHELDLAST),
    DEFINE_MESSAGE(WM_AFXFIRST),
    DEFINE_MESSAGE(WM_AFXLAST),
#endif /* WINVER >= 0x0400 */

#if(WINVER >= 0x0400)
    DEFINE_MESSAGE(WM_APP),
#endif /* WINVER >= 0x0400 */

#if((_WIN32_WINNT >= 0x0400) || (WINVER >= 0x0500))
    DEFINE_MESSAGE(WM_MOUSEHOVER),
    DEFINE_MESSAGE(WM_MOUSELEAVE),
#endif

#if(WINVER >= 0x0500)
    DEFINE_MESSAGE(WM_NCMOUSEHOVER),
    DEFINE_MESSAGE(WM_NCMOUSELEAVE),
#endif /* WINVER >= 0x0500 */

#if(WINVER >= 0x0400)
    DEFINE_MESSAGE(WM_IME_SETCONTEXT),
    DEFINE_MESSAGE(WM_IME_NOTIFY),
    DEFINE_MESSAGE(WM_IME_CONTROL),
    DEFINE_MESSAGE(WM_IME_COMPOSITIONFULL),
    DEFINE_MESSAGE(WM_IME_SELECT),
    DEFINE_MESSAGE(WM_IME_CHAR),
#endif /* WINVER >= 0x0400 */

#if(WINVER >= 0x0500)
    DEFINE_MESSAGE(WM_IME_REQUEST),
#endif /* WINVER >= 0x0500 */

#if(WINVER >= 0x0400)
    DEFINE_MESSAGE(WM_IME_KEYDOWN),
    DEFINE_MESSAGE(WM_IME_KEYUP),
#endif /* WINVER >= 0x0400 */

    DEFINE_MESSAGE(WM_SYSTIMER),

    { 0, NULL, }    // end of message list
};

void Msg_Init(WindowMsgLoop::MSGMAP& msgMap)
{
    for (const X_MAP_MESSAGE* pMsg = allMessages; pMsg->lpszMsg != NULL; pMsg++)
        msgMap.insert(std::make_pair(pMsg->nMsg, pMsg->lpszMsg));
}

LPCTSTR WindowMsgLoop::DebugGetMessageName(UINT message)
{
    static TCHAR msgBuffer[255];
    if (message >= 0xC000)
    {
        // Window message registered with 'RegisterWindowMessage'
        //  (actually a USER atom)
        if (GetClipboardFormatName(message, msgBuffer, sizeof(msgBuffer) * sizeof(TCHAR)))
            return msgBuffer;
    }
    auto found = m_msgMap.find(message);
    if (found != m_msgMap.end())
        return found->second;
    else
        return _T("UNKNOWN");
}

void WindowMsgLoop::SetEventBase(event_base* base)
{
    evbase = base;
}
